﻿using IA.Shopping.UI;
using IA.Shopping.UI.Utils;
using ProjetoIA_Shopping.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ProjetoIA_Shopping
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private List<Thread> _threads = null;
        //private int qtdAgentesCompleted = 0;
        private bool _inicializado = false;
        private List<Object> _listaExecucao = null;
        List<KeyValuePair<string, double>> _list = new List<KeyValuePair<string, double>>();
        DateTime _inicio;
        DateTime _fim;

        public MainWindow()
        {
            InitializeComponent();

            this._listaExecucao = new List<object>();
            this._threads = new List<Thread>();

            Shopping.Inicializar();
            this.Dispatcher.Invoke(() => Shopping.CriarUI(this.grdShopping));
            this.SizeChanged += grdShopping_SizeChanged;

            Loja.ProdutoComprado += Loja_ProdutoComprado;
            this._inicializado = true;

            this.Loaded += MainWindow_Loaded;
        }

        #region Inicializar Agentes e Threads

        private void InicializarThreads()
        {
            foreach (var agente in Shopping.Agentes)
            {
                this._threads.Add(new Thread(agente.BuscaEmProfundidade));
            }

            foreach (var thread in this._threads)
            {
                thread.Start();
            }
        }

        private void InicializarAgentes()
        {
            var qtdAgentes = Int32.Parse((this.cboQuantidadeAgentes.SelectedValue as ListBoxItem).Content.ToString());
            var dinheiroParaCadaAgente = AgenteUtil.DINHEIRO_TOTAL / qtdAgentes;
            for (int i = 0; i < qtdAgentes; i++)
            {
                if (i == 0)
                    Shopping.Agentes.Add(new Agente(new Point(0, 0), i + 1, dinheiroParaCadaAgente));
                else if (i == 1)
                    Shopping.Agentes.Add(new Agente(new Point(0, Util.DIMENSAO_MATRIZ - 1), i + 1, dinheiroParaCadaAgente));
                else if (i == 2)
                    Shopping.Agentes.Add(new Agente(new Point(Util.DIMENSAO_MATRIZ - 1, 0), i + 1, dinheiroParaCadaAgente));
                else
                    Shopping.Agentes.Add(new Agente(new Point(Util.DIMENSAO_MATRIZ - 1, Util.DIMENSAO_MATRIZ - 1), i + 1, dinheiroParaCadaAgente));

                Shopping.Agentes[i].PositionChanged += Agente_OnPositionChanged;
                Shopping.Agentes[i].BuscaCompleted += Agente_OnBuscaCompleted;
            }
            Shopping.QTDAgentes = Shopping.Agentes.Count;
        }

        #endregion

        private void Resetar(bool novoShopping)
        {
            ResetarAgentes();

            Dispatcher.Invoke(() =>
            {
                this.ApagarDinheiroAgente();
                this.ButtonsEnable(true);
            });

            if (novoShopping)
            {
                Shopping.Matriz = null;
                Shopping.Lojas.Clear();
                Shopping.Inicializar();

                this.Dispatcher.Invoke(() =>
                {
                    this.grdShopping.Children.Clear();
                    Shopping.CriarUI(this.grdShopping);
                });
            }
            else
            {
                var qtdElementosGrid = Dispatcher.Invoke(() => this.grdShopping.Children.Count);
                var qtdElementos = (Util.DIMENSAO_MATRIZ * Util.DIMENSAO_MATRIZ) + Util.QUANTIDADE_LOJAS;

                if (qtdElementosGrid > qtdElementos)
                {
                    for (int i = qtdElementos; i < qtdElementosGrid; i++)
                    {
                        Dispatcher.Invoke(() => this.grdShopping.Children.RemoveAt(this.grdShopping.Children.Count - 1));
                    }

                    for (int i = 0; i < qtdElementos; i++)
                    {
                        Dispatcher.Invoke(() =>
                        {
                            var elemento = this.grdShopping.Children[i];
                            if (elemento.GetType() == typeof(Rectangle))
                            {
                                var rect = elemento as Rectangle;
                                rect.Fill = new SolidColorBrush(Colors.White);
                                Shopping.LimparMatriz();
                            }
                        });
                    }
                }
            }
            this.FinalizarThreads(false);
        }

        private static void ResetarAgentes()
        {
            Shopping.Agentes.Clear();
            Shopping.Agentes = new List<Agente>();
            GC.Collect();
            GC.WaitForFullGCComplete();
        }

        private void ApagarDinheiroAgente()
        {
            this.txtblkDinheiroAgnete1.Text = String.Empty;
            this.txtblkDinheiroAgnete2.Text = String.Empty;
            this.txtblkDinheiroAgnete3.Text = String.Empty;
            this.txtblkDinheiroAgnete4.Text = String.Empty;

            this.lstvItensComprados.Items.Clear();
        }

        private void MoverAgente(AgenteEventArgs e, Rectangle robo)
        {
            this.PintarBloco(e, robo);

            int qtdItensNoGrid = this.grdShopping.Children.Count;
            int qtdMinimaDeItens = (Util.DIMENSAO_MATRIZ * Util.DIMENSAO_MATRIZ) + Util.QUANTIDADE_LOJAS;

            Grid.SetRow(robo, (int)e.Posicao.X);
            Grid.SetColumn(robo, (int)e.Posicao.Y);

            if (qtdItensNoGrid == qtdMinimaDeItens)
                this.grdShopping.Children.Add(robo);
            else if (qtdItensNoGrid > qtdMinimaDeItens)
            {
                for (int i = qtdItensNoGrid; i > qtdMinimaDeItens; i--)
                {
                    var corRoboAtual = (robo.Fill as SolidColorBrush).Color;
                    var corRoboGrid = ((this.grdShopping.Children[i - 1] as Rectangle).Fill as SolidColorBrush).Color;

                    if (corRoboGrid.Equals(corRoboAtual))
                    {
                        this.grdShopping.Children.RemoveAt(i - 1);
                        break;
                    }
                }
                this.grdShopping.Children.Add(robo);
            }
        }

        private void PintarBloco(AgenteEventArgs e, Rectangle robo)
        {
            int position;
            if (e.Posicao.X == 0)
                position = (int)e.Posicao.Y;
            else
                position = (int)(((e.Posicao.X) * Util.DIMENSAO_MATRIZ) + e.Posicao.Y);
            switch (e.BlocoCor)
            {
                case Cor.BRANCO:
                    (this.grdShopping.Children[position] as Rectangle).Fill = new SolidColorBrush(Colors.White);
                    break;
                case Cor.CINZA:
                    (this.grdShopping.Children[position] as Rectangle).Fill = new SolidColorBrush(Colors.Gray);
                    break;
                case Cor.PRETO:
                    (this.grdShopping.Children[position] as Rectangle).Fill = new SolidColorBrush(Colors.Black);
                    break;
            }
        }

        #region Eventos Agente

        private void Agente_OnPositionChanged(object sender, AgenteEventArgs e)
        {
            Dispatcher.Invoke(() =>
                    {
                        Rectangle robo = null;

                        switch (e.AgenteID)
                        {
                            case 1:
                                this.txtblkDinheiroAgnete1.Text = " R$" + e.Dinheiro.ToString();
                                robo = ImagemUtil.CriarRobo(Colors.SandyBrown);
                                break;
                            case 2:
                                this.txtblkDinheiroAgnete2.Text = " R$" + e.Dinheiro.ToString();
                                robo = ImagemUtil.CriarRobo(Colors.SkyBlue);
                                break;
                            case 3:
                                this.txtblkDinheiroAgnete3.Text = " R$" + e.Dinheiro.ToString();
                                robo = ImagemUtil.CriarRobo(Colors.Wheat);
                                break;
                            case 4:
                                this.txtblkDinheiroAgnete4.Text = " R$" + e.Dinheiro.ToString();
                                robo = ImagemUtil.CriarRobo(Colors.HotPink);
                                break;
                        }
                        this.MoverAgente(e, robo);
                    });
        }

        private void Agente_OnBuscaCompleted(object sender, bool completed)
        {
            if (completed)
            {
                this._fim = DateTime.Now;
                var tempoFinal = this._fim.Subtract(this._inicio).TotalMilliseconds;

                if (this._list.Count >= 4)
                {
                    this._list.Clear();
                    Dispatcher.Invoke(() =>
                    {
                        this.serieAgente1.ItemsSource = null;
                        this.serieAgente2.ItemsSource = null;
                        this.serieAgente3.ItemsSource = null;
                        this.serieAgente4.ItemsSource = null;
                    });
                }
                this._list.Add(new KeyValuePair<string, double>(Shopping.Agentes.Count.ToString(), tempoFinal));
                Dispatcher.Invoke(() => serieAgente1.ItemsSource = this._list);

                MessageBox.Show("Finalizado");
                this.Resetar(false);
            }
        }

        private void Loja_ProdutoComprado(object sender, Utils.Eventos.ProdutoCompradoEventArgs e)
        {
            var result = String.Format("Produto: {0} Valor: {1} Comprado pelo agente: {2}",
                e.ProdutoComprado._Nome, e.ProdutoComprado._Valor, e.AgenteID);

            Dispatcher.Invoke(() => this.lstvItensComprados.Items.Add(result));
        }

        #endregion

        #region Eventos UI

        private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            Util.TEMPO = e.NewValue;
        }

        private void cboQuantidadeAgentes_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            this.VisibilityPanelDinheiroAgente();
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            this.VisibilityPanelDinheiroAgente();
        }

        private void grdShopping_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (this._inicializado)
            {
                var width = (e.NewSize.Width / Util.DIMENSAO_MATRIZ) - 1;
                var height = ((e.NewSize.Height * 80 / 100) / Util.DIMENSAO_MATRIZ) - 2;
                Rectangle rect = null;

                for (int i = 0; i < Util.DIMENSAO_MATRIZ; i++)
                {
                    for (int j = 0; j < Util.DIMENSAO_MATRIZ; j++)
                    {
                        this.grdShopping.ColumnDefinitions[j].Width = new GridLength(width);
                        this.grdShopping.RowDefinitions[i].Height = new GridLength(height);
                    }
                }

                for (int i = 0; i < this.grdShopping.Children.Count; i++)
                {
                    rect = this.grdShopping.Children[i] as Rectangle;
                    if (rect != null)
                    {
                        rect.Height = height - 10;
                        rect.Width = width - 10;
                        rect = null;
                    }
                }
            }

        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            this.FinalizarThreads(true);
            base.OnClosing(e);
        }

        #region Botoes

        private void btnIniciar_Click(object sender, RoutedEventArgs e)
        {
            this._inicio = DateTime.Now;
            Dispatcher.Invoke(() => this.ButtonsEnable(false));

            this.InicializarAgentes();
            this.InicializarThreads();
        }

        private void btnGerarGrafico_Click(object sender, RoutedEventArgs e)
        {
            if (this.popUp.Visibility == System.Windows.Visibility.Visible)
            {
                this.popUp.Visibility = System.Windows.Visibility.Collapsed;
                this.btnIniciar.IsEnabled = true;
                this.btnResetar.IsEnabled = true;
            }
            else
            {
                this.popUp.Visibility = System.Windows.Visibility.Visible;
                this.btnIniciar.IsEnabled = false;
                this.btnResetar.IsEnabled = false;
            }

        }

        private void btnResetar_Click(object sender, RoutedEventArgs e)
        {
            this._list.Clear();
            Dispatcher.Invoke(() =>
            {
                this.serieAgente1.ItemsSource = null;
                this.serieAgente2.ItemsSource = null;
                this.serieAgente3.ItemsSource = null;
                this.serieAgente4.ItemsSource = null;
            });

            this.Resetar(true);
        }

        #endregion

        #endregion

        private void FinalizarThreads(bool isWindowClosing)
        {
            for (int i = 0; i < this._threads.Count; i++)
            {
                if (isWindowClosing)
                    this._threads[i].Abort();
                else
                    this._threads[i] = null;
            }
            this._threads.Clear();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        private void VisibilityPanelDinheiroAgente()
        {
            if (this._inicializado)
            {
                var qtdAgentesCBO = Int32.Parse((this.cboQuantidadeAgentes.SelectedItem as ComboBoxItem).Content.ToString());

                switch (qtdAgentesCBO)
                {
                    case 1:
                        this.panelDinheiroAgente1.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente2.Visibility = System.Windows.Visibility.Collapsed;
                        this.panelDinheiroAgente3.Visibility = System.Windows.Visibility.Collapsed;
                        this.panelDinheiroAgente4.Visibility = System.Windows.Visibility.Collapsed;
                        break;
                    case 2:
                        this.panelDinheiroAgente1.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente2.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente3.Visibility = System.Windows.Visibility.Collapsed;
                        this.panelDinheiroAgente4.Visibility = System.Windows.Visibility.Collapsed;
                        break;
                    case 3:
                        this.panelDinheiroAgente1.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente2.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente3.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente4.Visibility = System.Windows.Visibility.Collapsed;
                        break;
                    case 4:
                        this.panelDinheiroAgente1.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente2.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente3.Visibility = System.Windows.Visibility.Visible;
                        this.panelDinheiroAgente4.Visibility = System.Windows.Visibility.Visible;
                        break;
                }
            }
        }

        private void ButtonsEnable(bool isEnable)
        {
            this.btnIniciar.IsEnabled = isEnable;
            this.btnGerarGrafico.IsEnabled = isEnable;
            this.btnResetar.IsEnabled = isEnable;
            this.cboQuantidadeAgentes.IsEnabled = isEnable;
        }
    }
}
